<?php

class Nuo
{

    static private $_resourceType = array(
        'table' => '.model.table.',
        'controller' => '.controller.',
        'block' => '.controller.block.',
        'plugin' => '.plugin.',
        'helper' => '.helper.',
        'form' => '.form.',
        'htmlElement' => '.html.element.',
        'htmlRender' => '.html.render.'
    );
    /**
     * @var
     */
    static private $_vars = array(
        'theme' => 'admin',
        'siteId' => 1,
        'baseUrl' => BASE_URL,
        'baseHref' => BASE_HREF,
    );

    /**
     * define hook
     * @var array
     */
    static private $_listeners = array();

    /**
     * @var array
     */
    static private $_helpers = array();

    /**
     * @var array
     */
    static private $_tables = array();

    /**
     * @var array
     */
    static private $_kinds = array();

    /**
     * @var string
     */
    static private $_tablePrefix = 'nuo_';

    /**
     * alias of resource.
     * @var array
     */
    static private $_alias = array();

    /**
     * @var user
     */
    static private $_viewer = null;

    /**
     * @var Request
     */
    static private $_request = null;

    /**
     * get response
     */
    static private $_response = null;

    /**
     * initialize
     * @return void
     */
    static public function bootstrap()
    {
        spl_autoload_register(array(
            'Nuo',
            '__autoload'
        ));

        // Register
        $log = Nuo::getHelper('@log');

        set_error_handler(array(
            $log,
            'handleError'
        ));

        set_exception_handler(array(
            $log,
            'handleException'
        ));

        self::$_listeners =
        include NUO_SETTING_PATH . '/listener.php';

        self::$_kinds =
        include NUO_SETTING_PATH . '/kind.php';

        self::initDb();

        self::initSession();

    }

    static public function initSession()
    {
        $session = Nuo::getTable('core@session');
        Zend_Session::setSaveHandler($session);
        Zend_Session::start();
    }

    /**
     * init database
     */
    static public function initDb()
    {

        require_once 'Zend/Db.php';

        require_once 'Zend/Db/Table/Abstract.php';

        $db = Zend_Db::factory('pdo_mysql', array(
            'username' => 'nora',
            'password' => 'nora123',
            'dbname' => 'namnv_nuo',
            'port' => 3306,
            'host' => 'localhost'
        ));

        Zend_Db_Table_Abstract::setDefaultAdapter($db);

        if (null != ($cache = Nuo::getHelper('@cache') -> getCache('core')))
        {
            Zend_Db_Table_Abstract::setDefaultMetadataCache($cache);
        }
    }

    /**
     * @return string
     */
    static public function getTablePrefix()
    {
        return self::$_tablePrefix;
    }

    /**
     * quote get table name
     * @param string $name
     * @return string
     */
    static public function getTableName($name)
    {
        return self::$_tablePrefix . $name;
    }

    /**
     * @param string $name
     * @param mixed $default
     * @return void
     */
    static public function get($name, $default = null)
    {
        return isset(self::$_vars[$name]) ? self::$_vars[$name] : (self::$_vars[$name] = $default);
    }

    /**
     * @param string $name
     * @param mixed $value
     * @return void
     */
    static public function set($name, $value)
    {
        self::$_vars[$name] = $value;
    }

    /**
     * get api object
     * @param string $name, etc: core@testcollate
     * @return api
     */
    static public function getHelper($name)
    {
        return isset(self::$_helpers[$name]) ? self::$_helpers[$name] : (self::$_helpers[$name] = self::getResource($name, 'helper'));
    }

    /**
     * get table object
     * @param string $name
     * @return Model_Table
     */
    static public function getTable($name)
    {
        return isset(self::$_tables[$name]) ? self::$_tables[$name] : (self::$_tables[$name] = self::getResource($name, 'table'));
    }

    /**
     * @param string $name
     * @param string $spec
     * @return void
     */
    static public function setKind($name, $spec)
    {
        self::$_kinds[$name] = $spec;
    }

    /**
     * @var string $event
     * @param object $caller
     * @param array $params
     * @return void
     */
    static public function fireEvent(/*magicall*/)
    {
        $args = func_get_args();

        $name = array_shift($args);

        if (isset(self::$_listeners[$name]))
        {
            try
            {
                foreach ((array)self::$_listeners[$name] as $funciton)
                {
                    call_user_func_array($funciton, $args);
                }
            }
            catch(Exception $e)
            {

            }
        }
    }

    /**
     * alias should be called before process load resource, it's better at the
     * time of bootstrap request.
     * @param string $name
     * @param string $type
     * @return Object
     * @throws Exception
     */
    static public function getResource($name, $type)
    {

        $class = isset(self::$_alias[$type][$name]) ? self::$_alias[$type][$name] : (self::$_alias[$type][$name] = trim(implode('_', array_map('ucfirst', explode('.', str_replace('@', self::$_resourceType[$type], $name)))), '_'));
        return new $class;
    }

    /**
     * @return Form
     */
    static public function getForm($name)
    {
        return self::getResource($name, 'form');

    }

    /**
     * @return Html Element
     */
    static public function getHtmlElement($name)
    {
        return self::getResource($name, 'htmlElement');
    }

    /**
     * @param string $class
     * @return bool
     */
    static public function loadClass($class)
    {
        return class_exists($class, 0) or self::__autoload($class);
    }

    /**
     * @param string $class
     * @return bool
     */
    static public function __autoload($class)
    {
        if (file_exists($file = NUO_MODULE_PATH . '/' . strtolower(str_replace('_', '/', $class)) . '.php'))
        {
            require_once $file;

            if (class_exists($class, 0))
            {
                return 1;
            }
        }

        if (file_exists($file = NUO_CORE_PATH . '/' . strtolower(str_replace('_', '/', $class)) . '.php'))
        {
            require_once $file;

            if (class_exists($class, 0))
            {
                return 1;
            }
        }

        if (file_exists($file = NUO_INCLUDE_PATH . '/' . str_replace('_', '/', $class) . '.php'))
        {
            require_once $file;
            if (class_exists($class, 0))
            {
                return 1;
            }
        }

        return 0;
    }

    /**
     * @var string $uri
     * @return full string url.
     */
    static public function getUrl($uri, $params = null)
    {
        if ($params)
        {
            $uri .= '?' . http_build_query($params);
        }

        return self::get('baseHref') . $uri;
    }

    /**
     * get user
     * @example Nuo::getViewer()
     * @link: user_model_table_row_user
     * @return user_model_table_row_user
     */
    static public function getViewer()
    {

        if (null == self::$_viewer)
        {

            $user = NULL;

            $uid = isset($_SESSION['UID']) ? $_SESSION['UID'] : null;

            if ($uid)
            {
                $user = Nuo::getObject($uid, 'user@user');
            }

            if (!is_object($user))
            {
                $user = new user_model_table_row_user();
            }

            self::$_viewer = $user;
        }

        return self::$_viewer;
    }

    /**
     * @return void
     */
    static public function setViewer($user)
    {
        self::$_viewer = $user;
    }

    /**
     * @return Request
     */
    static public function getRequest()
    {
        if (null == self::$_request)
        {
            if ('cli' == php_sapi_name())
            {
                require_once 'request/cli.php';
                self::$_request = new Request_Cli;
            }
            else
            {
                require_once 'request/http.php';
                self::$_request = new Request_Http;
            }
        }
        return self::$_request;
    }

    /**
     * @return Response
     */
    static public function getResponse()
    {
        if (null == self::$_response)
        {
            require_once 'response.php';
            self::$_response = new Response;
        }
        return self::$_response;
    }

    /**
     * getunique UUID string 12 byte
     * first 6 byte from microtime
     * next 2 byte from kind id
     * next 4 byte of random value
     * @kind id default = 0
     */
    static public function UUID($kind)
    {
        // 0x01b21dd213814000 is the number of 100-ns intervals between the
        // UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
        $kindId = array_search($kind, self::$_kinds);

        if (!$kindId)
        {
            throw new Exception("Invalid Kind");
        }

        $time = microtime(1);
        $bin = pack("NnnN", $time, $time * 10000>>32, intval($kindId), mt_rand(0, 0xFFFFFF));
        $hex = unpack("H*hex", $bin);
        return array_pop($hex);
    }

    static public function getObject($uuid, $kind = null)
    {
        if (null == $kind)
        {
            $kindId = hexdec(substr($uuid, 12, 4));

            if (!isset(self::$_kinds[$kindId]))
            {
                throw new Exception("Invalid Kind Id");
            }

            $kind = self::$_kinds[$kindId];
        }

        return Nuo::getTable($kind) -> find($uuid) -> current();
    }

    static public function getKind($uuid)
    {
        $kindId = hexdec(substr($uuid, 12, 4));

        if (!isset(self::$_kinds[$kindId]))
        {
            throw new Exception("Invalid Kind Id");
        }

        return self::$_kinds[$kindId];
    }

    static public function getKindId()
    {
        // 0x01b21dd213814000 is the number of 100-ns intervals between the
        // UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
        $kindId = array_search($kind, self::$_kinds);

        if (!$kindId)
        {
            throw new Exception("Invalid Kind");
        }

        return $kindId;
    }

}

/**
 * @param string $string
 * @return string
 */
function inflect_string($string)
{
    return str_replace(' ', '', ucwords(str_replace(array(
        '.',
        '-'
    ), ' ', $string)));
}

/**
 * @param string $string
 * @return string
 */
function deflect_string($string)
{
    return strtolower(trim(preg_replace('/([a-z0-9])([A-Z])/', '\1-\2', $string), '-. '));
}
